from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from django.core import validators
from fiteat.middleware import threadlocals
from fiteat.utils import get_units, truncate, flatten, save_with_trends, Constants, K
from django.db.models.options import AdminOptions
import re
import copy
from fiteat.apps.main.models import Profile

CARDIO_TYPES = (
    ('walking',                 _('Walking')),
    ('running',                 _('Running')),
    ('jogging',                 _('Jogging')),
    ('treadmill',               _('Treadmill')),
    ('elliptical',              _('Elliptical')),
    ('stepper',                 _('Stepper')),
    ('cross-country bicycle',   _('Cross-country bicycle')),
    ('road bicycle',            _('Road bicycle')),
    ('stationary bicycle',      _('Stationary bicycle')),
    ('other',                   _('Other')),
)

INTENSITY_CHOICES = (
    ('LISS', _('LISS')),
    ('HIIT', _('HIIT')),
)

custom_fields = ['custom1', 'custom2', 'custom3', 'custom4', 'custom5']
trendable_fields = ['duration', 'distance', 'avg_hr', 'max_hr', 'calories'] + custom_fields
trend_fields = flatten([(x, '%s_trend' % x) for x in trendable_fields])

class Cardio(models.Model):
    user = models.ForeignKey(User, blank=True, editable=False)
    date = models.DateField(_('date'))
    workout_type = models.CharField(_('Type'), choices=CARDIO_TYPES, max_length=50)
    other_workout_type = models.CharField(_('Other'), max_length=50, blank=True, validator_list=[validators.RequiredIfOtherFieldEquals('worjout_type', 'other')])
    intensity = models.CharField(_('Intensity'), choices=INTENSITY_CHOICES, max_length=50)
    duration = models.IntegerField(_('Duration (mins)'), blank=True, null=True)
    duration_trend = models.IntegerField(_('Duration\'s trend (mins)'), blank=True, null=True)
    distance = models.DecimalField(_('Distance'), max_digits=4, decimal_places=2, blank=True, null=True)
    distance_trend = models.DecimalField(_('Distance\'s trend'), max_digits=4, decimal_places=2, blank=True, null=True)
    avg_hr = models.IntegerField(_('Average HR'), blank=True, null=True)
    avg_hr_trend = models.IntegerField(_('Average HR\'s trend'), blank=True, null=True)
    max_hr = models.IntegerField(_('Maximum HR'), blank=True, null=True)
    max_hr_trend = models.IntegerField(_('Maximum HR\'s trend'), blank=True, null=True)
    calories = models.IntegerField(_('Calories'), blank=True, null=True)
    calories_trend = models.IntegerField(_('Calories\' trend'), blank=True, null=True)
    comment = models.CharField(_('Comment'), max_length=200, blank=True, null=True)
    custom1 = models.DecimalField(_('custom1'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom1_trend = models.DecimalField(_('custom1\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom2 = models.DecimalField(_('custom2'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom2_trend = models.DecimalField(_('custom2\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom3 = models.DecimalField(_('custom3'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom3_trend = models.DecimalField(_('custom3\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom4 = models.DecimalField(_('custom4'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom4_trend = models.DecimalField(_('custom4\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom5 = models.DecimalField(_('custom5'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom5_trend = models.DecimalField(_('custom5\'s trend'), max_digits=5, decimal_places=2, blank=True, null=True)

    def __unicode__(self):
        return _("Your cardio workout for %(date)s") % ({'date': self.date})
    
    def save(self):
        save_with_trends(self, Cardio, trendable_fields)

    def workout_type_or_other(self):
        if self.workout_type == 'other' or not self.workout_type:
            return self.other_workout_type
        return self.workout_type

    workout_type_or_other.short_description = _('Type')

    def cals_per_min(self):
        if not self.duration or self.duration == 0:
            return 0
        return round(self.calories / self.duration, 2)

    cals_per_min.short_description = _('Calories per minute')

    def cals_per_dist(self):
        if not self.distance or self.distance == 0:
            return 0
        return round(self.calories / self.distance, 2)

    cals_per_dist.short_description = _('Calories per distance unit')

    def avg_speed(self):
        if not self.duration or self.duration == 0:
            return 0
        return round(self.distance * 60 / self.duration, 2)

    avg_speed.short_description = _('Average speed')

    class Meta:
        verbose_name = _('cardio workout')
        verbose_name_plural = _('cardio workouts')

    class Admin:
        date_hierarchy = 'date'
        list_display = ('date',)
        ordering = ('-date',)
        fields = (
            (None, {
                'fields': ('date', ('workout_type', 'other_workout_type'), 'intensity', 'duration', 'distance', 'avg_hr', 'max_hr', 'calories', 'comment')
            }),
        )
        js = ('/fiteat/media/js/handle_other_workout_field.js',)

UNIT_CHOICES = (
    ('M', _('Metric')),
    ('E', _('English'))
)

FIELD_CHOICES = (
    ('intensity', _('Intensity')),
    ('duration', _('Duration')),
    ('distance', _('Distance')),
    ('avg_hr', _('Average HR')),
    ('max_hr', _('Max HR')),
    ('calories', _('Calories')),
    ('custom1', _('Custom1')),
    ('custom2', _('Custom2')),
    ('custom3', _('Custom3')),
    ('custom4', _('Custom4')),
    ('custom5', _('Custom5')),
)

class UserPref(models.Model):
    user = models.ForeignKey(User, blank=True, editable=False, unique=True)
    profile = models.ForeignKey(Profile, edit_inline=models.STACKED, unique=True, num_in_admin=1, related_name="cardio_profile", max_num_in_admin=1)
    workout_type_or_other = models.BooleanField(_('Workout Type'), default=True, core=True)
    intensity = models.BooleanField(_('Intensity'), default=True)
    duration = models.BooleanField(_('Duration (mins)'), default=False)
    duration_trend = models.BooleanField(_('Duration\'s trend (mins)'), default=False)
    distance = models.BooleanField(_('Distance'), default=False)
    distance_trend = models.BooleanField(_('Distance\'s trend'), default=False)
    avg_hr = models.BooleanField(_('Average HR'), default=False)
    avg_hr_trend = models.BooleanField(_('Average HR\'s trend'), default=False)
    max_hr = models.BooleanField(_('Maximum HR'), default=False)
    max_hr_trend = models.BooleanField(_('Maximum HR\'s trend'), default=False)
    calories = models.BooleanField(_('Calories'), default=False)
    calories_trend = models.BooleanField(_('Calories\' trend'), default=False)
    comment = models.BooleanField(_('Comment'), default=False)
    custom1 = models.BooleanField(_('custom1'), default=False)
    custom1_trend = models.BooleanField(_('custom1\'s trend'), default=False)
    custom2 = models.BooleanField(_('custom2'), default=False)
    custom2_trend = models.BooleanField(_('custom2\'s trend'), default=False)
    custom3 = models.BooleanField(_('custom3'), default=False)
    custom3_trend = models.BooleanField(_('custom3\'s trend'), default=False)
    custom4 = models.BooleanField(_('custom4'), default=False)
    custom4_trend = models.BooleanField(_('custom4\'s trend'), default=False)
    custom5 = models.BooleanField(_('custom5'), default=False)
    custom5_trend = models.BooleanField(_('custom5\'s trend'), default=False)
    custom1_caption = models.CharField(_('custom1 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom1')])
    custom2_caption = models.CharField(_('custom2 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom2')])
    custom3_caption = models.CharField(_('custom3 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom3')])
    custom4_caption = models.CharField(_('custom4 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom4')])
    custom5_caption = models.CharField(_('custom5 caption'), max_length=30, blank=True, validator_list=[validators.RequiredIfOtherFieldGiven('custom5')])

    duration_goal = models.IntegerField(_('Duration goal (mins)'), blank=True, null=True)
    distance_goal = models.DecimalField(_('Distance goal'), max_digits=4, decimal_places=2, blank=True, null=True)
    avg_hr_goal = models.IntegerField(_('Average HR goal'), blank=True, null=True)
    max_hr_goal = models.IntegerField(_('Maximum HR goal'), blank=True, null=True)
    calories_goal = models.IntegerField(_('Calories goal'), blank=True, null=True)
    custom1_goal = models.DecimalField(_('custom1 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom2_goal = models.DecimalField(_('custom2 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom3_goal = models.DecimalField(_('custom3 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom4_goal = models.DecimalField(_('custom4 goal'), max_digits=5, decimal_places=2, blank=True, null=True)
    custom5_goal = models.DecimalField(_('custom5 goal'), max_digits=5, decimal_places=2, blank=True, null=True)

    custom_chart1_field1 = models.CharField(_("Field #1"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart1_field2 = models.CharField(_("Field #2"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart1_field3 = models.CharField(_("Field #3"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)

    custom_chart2_field1 = models.CharField(_("Field #1"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart2_field2 = models.CharField(_("Field #2"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart2_field3 = models.CharField(_("Field #3"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)

    custom_chart3_field1 = models.CharField(_("Field #1"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart3_field2 = models.CharField(_("Field #2"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)
    custom_chart3_field3 = models.CharField(_("Field #3"), max_length=30, choices=FIELD_CHOICES, blank=True, null=True)

    def __unicode__(self):
        return str(_("Your cardio settings"))

    def save(self):
        self.user = threadlocals.get_current_user()
        super(UserPref,self).save()

    class Admin:
        fields = (
            (_('The units system'), {
                'fields': ('units_system',)
            }),
            (_('Fields to show in the tabular view'), {
                'fields': trend_fields
            }),
            (_('Captions of the custom fields'), {
                'classes': 'collapse',
                'fields': ('custom1_caption','custom2_caption','custom3_caption','custom4_caption', 'custom5_caption', )
            }),
            (_('Goals'), {
                'classes': 'collapse',
                'fields': ('duration_goal', 'distance_goal', 'avg_hr_goal', 'max_hr_goal', 'calories_goal', 'custom1_goal', 'custom2_goal', 'custom3_goal', 'custom4_goal', 'custom5_goal',)
            }),
            (_('Custom chart #1'), {
                'classes': 'collapse',
                'fields': ('custom_chart1_field1', 'custom_chart1_field2', 'custom_chart1_field3',)
            }),
            (_('Custom chart #2'), {
                'classes': 'collapse',
                'fields': ('custom_chart2_field1', 'custom_chart2_field2', 'custom_chart2_field3', )
            }),
            (_('Custom chart #3'), {
                'classes': 'collapse',
                'fields': ('custom_chart3_field1', 'custom_chart3_field2', 'custom_chart3_field3', )
            }),
        )

    class Meta:
        verbose_name = _('cardio related settings')
        verbose_name_plural = _('cardio related settings')


class ApplicationAdminOptions(AdminOptions):
    def sort_workout_choices(self):
        from django.db import connection
        cursor = connection.cursor()
        cursor.execute("select workout_type, other_workout_type, count(*) as c from cardio_cardio group by other_workout_type, workout_type order by c desc;")
        types = dict()
        descs = dict()
        for name, desc in CARDIO_TYPES:
            types[name] = 0
            descs[name] = desc
        for row in cursor.fetchall():
            type, other, count = row
            if type != 'other' and type != '':
                types[type] += count
            elif other.lower() in types:
                types[other.lower()] += count
        """ Mark the types that were used by this user with asterisks """
        for type in types:
            if types[type] > 0:
                descs[type] = '* %s *' % descs[type]
        """ Sort the types by their count now """
        items = types.items()
        backitems = [[v[1], v[0]] for v in items]
        backitems.sort()
        backitems.reverse()
        sorted_types = [ backitems[i][1] for i in range(0,len(backitems))]
        choices = [(x, descs[x]) for x in sorted_types]
        return choices

    def _fields(self):
        fields = (
            (None, {
                'fields': ('date', ('workout_type', 'other_workout_type'), 'intensity', 'duration', 'distance', 'avg_hr', 'max_hr', 'calories', 'comment')
            }),
        )
        for field in Cardio._meta.fields:
            if field and field.name == 'workout_type':
                user = threadlocals.get_current_user()
                if user is None or user.is_anonymous():
                    break
                field._choices = self.sort_workout_choices()
        return fields

    fields = property(_fields)

    def _list_display(self):
        user = threadlocals.get_current_user()
        if user is None or user.is_anonymous():
            return ()

        fields = ['date',]
        prefs = user.get_profile().cardio_profile
        weight, length, distance = get_units(user.get_profile())
        for i in Cardio._meta.fields:
            if re.match('custom\d$', i.name):
                i.verbose_name = eval('prefs.%s_caption' % i.name)
            m = re.match('(?P<name>custom\d)_trend$', i.name)
            if m:
                i.verbose_name = eval('prefs.%s_caption' % m.group('name')) + "'s trend"
# TODO: Add caption for the custom fields' trend
        for attr in trend_fields + ['comment']:
            if eval('prefs.%s' % attr):
                fields.append(attr)
        return fields
    list_display = property(_list_display)

del Cardio._meta.admin.list_display
del Cardio._meta.admin.fields
Cardio._meta.admin.__class__ = ApplicationAdminOptions


